from  flask_classy import FlaskView, route
from flask import request
from ..schema.param import shop_cart_param, checkout_param, currency_param, create_shop_cart_param, \
    update_schop_cart_param
from ..schema.error import PRODUCT_NOT_EXIST, STOCK_NOT_EXIST, SERVICE_NOT_EXIST, SHOP_CART_NOT_EXIST, MEMBER_NOT_EXIST, \
    CART_ITEM_HAS_OWNER, CART_ITEM_STATUS_ERROR, CART_ITEM_NOT_EXIST, PARAMS_ERROR, CART_ITEM_NOT_BELONG_SHOP_CART, \
    PARAMS_DOES_NOT_EXIST, FUND_CHANNEL_NOT_EXIST

import datetime
from ..models.shop_cart import ShopCart, CartItem, CartItemAdditive
from ..models.product import Product
from ..models.offer import Offer
from ..models.service import Service
from ..models.currency import Currency
from ..models.member import Member
from ..models.order import Order, OrderDetail, OrderDetailExt
import shortuuid
import json
from ..schema.object import success_schema, currency_schema, \
    ShopCharItemSchema, order_schema, check_out_schema
from marshmallow.exceptions import ValidationError
from . import current_member, require_login
from  decimal import Decimal
from . import next_id  # TODO
import logging
from ..util import join_url
from flask import url_for

from ..contril import FundIn, FundInOrder, FundChannel, PaymentException

logger = logging.getLogger('vso.shop_cart')


class ShopCartsView(FlaskView):
    def create_order_detail_ext(self, additive, item_amount, item_count, currency):
        from ..models.order import OrderDetailExt
        logger.info('Calc additive [%s-%s]', additive.type, additive.ref_id)
        if additive.type == 'service':
            service = Service.first(Service.id == additive.ref_id)

            if service.mode == 'fixed':
                amount = round((service.fee * item_count) * currency.rate, 2)
            elif service.mode == 'rate':
                amount = round(((item_amount * service.fee) * item_count), 2)
            logger.info('  additive amount [%s]', amount)
            return OrderDetailExt(**{
                'count': item_count,
                'amount': round(amount, 2),
                'ext_type': 'service',
                'ext_code': service.id
            })

    def calc_cart_item_amount(self, currency, item):
        stock = item.stock
        item_amount = (item.stock.price * item.count) * currency.rate

        for service in item.services:
            item_amount += (service.fee * item.count) * currency.rate
        return round(item_amount, 2)

    def create_order_detail_and_ext(self, item, currency):
        detail = OrderDetail(
            product=item.product,
            offer=item.stock,
            count=item.count
        )
        order_detail_exts = []

        total_amount = Decimal.from_float(0.00)

        detail_amount = round((item.stock.price * item.count) * currency.rate, 2)

        total_amount = detail_amount
        if item.additives:
            for additive in item.additives:
                order_detail_ext = self.create_order_detail_ext(
                    additive,
                    detail_amount,
                    item.count,
                    currency
                )
                order_detail_exts.append(order_detail_ext)
                total_amount += order_detail_ext.amount
        detail.amount = total_amount
        detail.real_amount = total_amount

        return detail, order_detail_exts

    @route('/checkout', methods=['POST'])
    @require_login
    def checkout(self):

        data = request.get_json(force=True)
        logger.debug('checkout shop_cart , request : {}'.format(data))

        member = current_member()
        currency = Currency.get(Currency.id == data.get('currency'))
        cart_items = data.get('cart_items')
        notify_info = data.get('notify_info', {})
        fund_channel = self._get_fund_channel(data.get('fund_channel'))

        if not fund_channel:
            logger.error('Cant not find fund channel:{}'.format(data.get('fund_channel')))
            return FUND_CHANNEL_NOT_EXIST.jsonify()

        shop_cart = ShopCart.get_by_member(member)
        if not shop_cart:
            shop_cart = ShopCart.create(id=shortuuid.uuid().upper(), member=member)

        logger.info('Begin shop cart checkout, cart item ids :{}'.format(cart_items))

        total_amount = Decimal.from_float(0.00)

        order_details = []

        item_list = CartItem.select().where(CartItem.id.in_(cart_items))

        order_amount = Decimal.from_float(0.00)
        for item in item_list:
            if not item.shop_cart == shop_cart:
                return CART_ITEM_NOT_BELONG_SHOP_CART.jsonify()
            detail, detail_ext = self.create_order_detail_and_ext(item, currency)

            discount_amount = Decimal.from_float(0.00)  # TODO

            detail.real_amount = detail.amount - discount_amount

            order_details.append((detail, detail_ext, item))

            order_amount += detail.real_amount

        order_id = next_id('ORDER')

        if 'link' in notify_info:
            try:
                notify_info['link'] = notify_info['link'].format(order_id=order_id)
            except Exception as es:
                pass

        description = '微搜游戏订单 #{}'.format(order_id)
        order = Order(
            id=order_id,
            member=member,
            description=description,
            trade_code='{}.{}'.format(datetime.datetime.now().strftime('%Y%m%d%H%M%S'), order_id),
            payment_method=fund_channel.id,
            currency=currency,
            notify_info=json.dumps(notify_info)

        )

        # TODO 折扣
        discount_amount = Decimal.from_float(0.00)

        order.amount = order_amount  # 订单计算金额
        order.real_amount = order_amount - discount_amount  # 实际金额 = 计算金额 - 折扣金额
        order.discount_amount = discount_amount

        # 手续费 = 订单实际金额 * 费率
        order.fee = round(Decimal.from_float(fund_channel.fee_rate) * order.real_amount / 100, 2)

        logger.info(
            ' order  total  amount  : {}  discount amount : {} ,real_amount is {} ,fee {}:' \
                .format(order.amount,
                        order.discount_amount,
                        order.real_amount, order.fee,
                        ))

        with Order.atomic():
            # 创建订单
            order.save(force_insert=True)
            # 创建订单明细
            for order_detail, order_detail_exts, cart_item in order_details:
                # order_detail.offer = offer
                order_detail.order = order
                order_detail.save(force_insert=True)
                if order_detail_exts:
                    for ext in order_detail_exts:
                        ext.order_detail = order_detail
                        ext.save(force_insert=True)
                # 将购物车条目的状态设置为已生成订单
                cart_item.status = 'ordered'
                cart_item.save()
        logger.info('Order [%s] created success. ', order_id)
        notify_info = {
            'method': 'REDIRECT',
            'link': join_url(request.url_root, url_for('OrderResultView:on_payment_notify'))
        }
        fund_in_order = FundInOrder(
            amount=float(order.amount),
            fund_channel=fund_channel.id,
            currency=currency.id,
            notify_info=notify_info,
            trade_code=order.trade_code,
            description=order.description)

        try:
            fundIn = FundIn.create(fund_in_order)
            if fundIn.success:
                logger.info('check out success [%s]', fundIn)

                order.payment_seq = fundIn.id
                order.save()
                return check_out_schema.jsonify(fundIn)
            else:
                print(fundIn.error)
        except PaymentException as pe:
            pass

        return success_schema.jsonify(
            order_schema.dump(order).data
        )

    def _get_fund_channel(self, fund_channel):
        logger.info('Get FundChannel , FundChannel Id: {}'.format(fund_channel))
        return FundChannel.find(fund_channel)

    @route('/member/<member_id>', methods=['POST'])
    @require_login
    def update_member_shop_cart(self, member_id):
        member = current_member()
        if not member.id == member_id:
            pass
        shop_cart = ShopCart.get_by_member(member)
        if not shop_cart:
            return SHOP_CART_NOT_EXIST.jsonify()
        if request.data:
            params, errors = update_schop_cart_param.loads(request.data.decode(request.charset))

            if not errors:
                try:
                    items = params.get('items') or []

                    exists_ids = [x.id for x in shop_cart.items]
                    new_ids = [x.get('id') for x in items]

                    # 删除掉已经删除的
                    for remove_id in list((set(exists_ids) - set(new_ids))):
                        CartItem.update(status='removed').where(CartItem.id == remove_id).execute()

                    for item in items:
                        count = item.get('count', 1)
                        item_id = item.get('id', -1)

                        cart_item = CartItem.get_or_none(CartItem.id == item_id)

                        if not cart_item:
                            return CART_ITEM_NOT_EXIST.jsonify()
                            # product = Product.get(Product.id == item.get('product'))
                            #
                            # stock = Stock.get(Stock.id == item.get('stock'))
                            # cart_item = CartItem.get_available_item(shop_cart, product, stock) \
                            #             or CartItem.create(product=product, stock=stock, count=0, shop_cart=shop_cart)
                        else:
                            if not cart_item.shop_cart == shop_cart:
                                return CART_ITEM_NOT_BELONG_SHOP_CART.jsonify()
                            cart_item.count = count

                            new_servcie_ids = item.get('services') or []

                            exists_servcie_ids = [s.id for s in cart_item.additives] or []

                            for s in list(set(exists_servcie_ids) - set(new_servcie_ids)):
                                CartItemAdditive.delete().where(CartItemAdditive.id == s)

                            for s in new_servcie_ids:
                                if s not in exists_servcie_ids:
                                    CartItemAdditive.create(**{
                                        'cart_item': cart_item,
                                        'title': Service.get(Service.id == s),
                                        'type': 'service'
                                    })

                            cart_item.save()
                    return success_schema.jsonify({})
                except Product.DoesNotExist as  pdne:
                    return PRODUCT_NOT_EXIST.jsonify()
                except Offer.DoesNotExist  as  sdne:
                    return STOCK_NOT_EXIST.jsonify()
                except Service.DoesNotExist as  sdne:
                    return SERVICE_NOT_EXIST.jsonify()
                except ShopCart.DoesNotExist as scdne:
                    return SHOP_CART_NOT_EXIST.jsonify()



        else:
            return PARAMS_DOES_NOT_EXIST.jsonify()

    @route('/member/<member_id>', methods=['GET'])
    @require_login
    def get_member_shop_cart(self, member_id):
        member = current_member()
        if not member.id == member_id:
            pass  # 无权限查看其他用户购物车

        params, _ = currency_param.load(request.values)

        currency = params.get('currency') or Currency.get_main_currency()

        shop_cart_items_schema = ShopCharItemSchema(many=True, context={'currency': currency})

        shop_cart_items_schema.context = {'currency': currency}
        shop_cart = ShopCart.get_by_member(member)
        return success_schema.jsonify(
            {
                'currency': currency_schema.dump(currency).data,
                'cart_items': shop_cart_items_schema.dump(
                    shop_cart.items).data
            }
        )

    @route('/<id>')
    def fetch_by_id(self, id):
        try:
            shop_cart = ShopCart.get(ShopCart.id == id)

            if shop_cart:
                params, errors = currency_param.load(request.values)

                currency = params.get('currency') or Currency.get_main_currency()

                shop_cart_items_schema = ShopCharItemSchema(many=True, context={'currency': currency})

                return success_schema.jsonify({
                    'currency': currency_schema.dump(currency).data,
                    'cart_items': shop_cart_items_schema.dump(shop_cart.items).data
                })
        except ShopCart.DoesNotExist as e:
            return SHOP_CART_NOT_EXIST.jsonify()

    # @route('/', methods=['POST'])
    # def create_shop_cart(self):



    @route('/', methods=['POST'])
    def create_shop_cart(self):
        if request.data:
            try:
                params, _ = create_shop_cart_param.loads(request.data.decode(request.charset))

                member = Member.get_member(params.get('member'))
                if not member:
                    return MEMBER_NOT_EXIST.jsonify()
                shop_cart, _ = ShopCart.get_or_create(member=member,
                                                      defaults={
                                                          'member': member,
                                                          'id': shortuuid.uuid(),
                                                          'updated_at': datetime.datetime.now()
                                                      })
                # cart_items = params.get('cart_items', [])
                items_from = params.get('items_from', None)

                cart_items = CartItem.select().where(CartItem.source == items_from)

                # 原有产品需要与现有产品合并
                for cart_item_id in cart_items:
                    cart_item = CartItem.get_or_none(CartItem.id == cart_item_id)
                    if not cart_item:
                        return CART_ITEM_NOT_EXIST.jsonify()
                    # if not cart_item.status == 'normal':
                    #     return CART_ITEM_STATUS_ERROR.jsonify()
                    if cart_item.shop_cart:
                        if not cart_item.shop_cart == shop_cart:
                            return CART_ITEM_HAS_OWNER.jsonify()
                        else:
                            continue

                    exist_cart_item = CartItem.get_available_item(shop_cart, cart_item.product,
                                                                  cart_item.stock, )

                    # 原来存在相同产品 ,则合并
                    if exist_cart_item:
                        exist_cart_item.count += cart_item.count
                        exist_cart_item.save()
                        # 将被合并的条目状态修改为 已合并
                        cart_item.status = 'merged'
                        cart_item.save()
                    else:
                        # 更新条目的购物车为当前购物车
                        cart_item.shop_cart = shop_cart
                        cart_item.save()
                return success_schema.jsonify({
                    'shop_cart': shop_cart.id
                })

            except    ValidationError as ve:
                return PARAMS_ERROR.jsonify(ve)
